package com.meis.widget.spiderweb;

import cs.help.tool.Log;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;

import java.math.BigDecimal;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by wenshi on 2019/3/26.
 * Description https://github.com/HpWens/MeiWidgetView
 */
public class SpiderWebView extends Component implements Component.DrawTask,
        Component.TouchEventListener, Component.LayoutRefreshedListener {
    // 控件宽高
    private int mWidth;
    private int mHeight;
    // 画笔
    private Paint mPointPaint;
    private Paint mLinePaint;
    private Paint mTouchPaint;
    // 触摸点坐标
    private float mTouchX = -1;
    private float mTouchY = -1;

    // 数据源
    private List<SpiderPoint> mSpiderPointList;
    // 相关参数配置
    private SpiderConfig mConfig;
    // 随机数
    private static SecureRandom mRandom=new SecureRandom();

    public SpiderWebView(Context context) {
        this(context, null);
    }

    public SpiderWebView(Context context, AttrSet attrs) {
        this(context, attrs, 0);
    }

    public SpiderWebView(Context context, AttrSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mSpiderPointList = new ArrayList<>();
        mConfig = new SpiderConfig();
        // 画笔初始化
        initPaint();
        setTouchEventListener(this::onTouchEvent);
        setLayoutRefreshedListener(this::onRefreshed);
        addDrawTask(this::onDraw);
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        mPointPaint = new Paint();
        mPointPaint.setAntiAlias(true);
        mPointPaint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);
        mPointPaint.setStrokeWidth(mConfig.pointRadius);
        mPointPaint.setColor(new Color(Color.getIntColor("#EBFF4081")));

        mLinePaint = new Paint();
        mLinePaint.setAntiAlias(true);
        mLinePaint.setStrokeWidth(mConfig.lineWidth);
        mLinePaint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);
        mLinePaint.setColor(new Color(Color.getIntColor("#EBFF94B9")));

        mTouchPaint = new Paint();
        mTouchPaint.setAntiAlias(true);
        mTouchPaint.setStrokeWidth(mConfig.touchPointRadius);
        mTouchPaint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);
        mTouchPaint.setColor(new Color(Color.getIntColor("#D8FF7875")));
    }

    /**
     * 初始化小点
     */
    private void initPoint() {
        for (int i = 0; i < mConfig.pointNum; i++) {
            int width = (int) (mRandom.nextFloat() * mWidth);
            int height = (int) (mRandom.nextFloat() * mHeight);
            SpiderPoint point = new SpiderPoint(width, height);
            int aX = 0;
            int aY = 0;
            // 获取加速度
            while (aX == 0) {
                aX = (int) ((mRandom.nextFloat() - 0.5F) * mConfig.pointAcceleration);
            }
            while (aY == 0) {
                aY = (int) ((mRandom.nextFloat() - 0.5F) * mConfig.pointAcceleration);
            }
            point.aX = aX;
            point.aY = aY;
            point.color = randomRGB();
            mSpiderPointList.add(point);
        }
    }

    @Override
    public void onRefreshed(Component component) {
        if (mSpiderPointList.size() == 0) {
            mWidth = component.getWidth();
            mHeight = component.getHeight();
            restart();
        }
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        canvas.save();
        // 绘制触摸点
        if (mTouchY != -1 && mTouchX != -1) {
            canvas.drawPoint(mTouchX, mTouchY, mTouchPaint);
        }
        if (mSpiderPointList != null && mSpiderPointList.size() > 0) {
            // 增强遍历
            int index = 0;
            for (SpiderPoint spiderPoint : mSpiderPointList) {
                spiderPoint.position[0] += spiderPoint.aX;
                spiderPoint.position[1] += spiderPoint.aY;
                // 越界反弹
                if (spiderPoint.position[0] <= mConfig.pointRadius) {
                    spiderPoint.position[0] = mConfig.pointRadius;
                    spiderPoint.aX = -spiderPoint.aX;
                } else if (spiderPoint.position[0] >= (mWidth - mConfig.pointRadius)) {
                    spiderPoint.position[0] = (mWidth - mConfig.pointRadius);
                    spiderPoint.aX = -spiderPoint.aX;
                }
                if (spiderPoint.position[1] <= mConfig.pointRadius) {
                    spiderPoint.position[1] = mConfig.pointRadius;
                    spiderPoint.aY = -spiderPoint.aY;
                } else if (spiderPoint.position[1] >= (mHeight - mConfig.pointRadius)) {
                    spiderPoint.position[1] = (mHeight - mConfig.pointRadius);
                    spiderPoint.aY = -spiderPoint.aY;
                }
                // 绘制触摸点与其他点的连线
                if (mTouchX != -1 && mTouchY != -1) {
                    int offsetX = (int) (mTouchX - spiderPoint.getPointX());
                    int offsetY = (int) (mTouchY - spiderPoint.getPointY());
                    int distance = (int) Math.sqrt(offsetX * offsetX + offsetY * offsetY);
                    if (distance < mConfig.maxDistance) {
                        if (distance >= (mConfig.maxDistance - mConfig.gravitation_strength)) {
                            if (spiderPoint.getPointX() > mTouchX) {
                                spiderPoint.position[0] -= 0.03F * -offsetX;
                            } else {
                                spiderPoint.position[0] += 0.03F * offsetX;
                            }

                            if (spiderPoint.position[1] > mTouchY) {
                                spiderPoint.position[1] -= 0.03F * -offsetY;
                            } else {
                                spiderPoint.position[1] += 0.03F * offsetY;
                            }
                        }
                        int alpha = (int) ((1.0F - (float) distance / mConfig.maxDistance) * mConfig.lineAlpha);
                        mLinePaint.setColor(new Color(spiderPoint.color));
                        mLinePaint.setAlpha(BigDecimal.valueOf(alpha).divide(BigDecimal.valueOf(255), 4, BigDecimal.ROUND_HALF_UP).floatValue());
                        canvas.drawLine(spiderPoint.position[0], spiderPoint.position[1], mTouchX, mTouchY, mLinePaint);
                    }
                }
                // 绘制小点
                mPointPaint.setColor(new Color(spiderPoint.color));
                canvas.drawCircle(spiderPoint.position[0], spiderPoint.position[1], mConfig.pointRadius, mPointPaint);
                // 绘制连线
                for (int i = index; i < mSpiderPointList.size(); i++) {
                    SpiderPoint point = mSpiderPointList.get(i);
                    // 判定当前点与其他点之间的距离
                    if (spiderPoint != point) {
                        int distance = disPos2d(point.position[0], point.position[1], spiderPoint.position[0], spiderPoint.position[1]);
                        if (distance < mConfig.maxDistance) {
                            // 绘制小点间的连线
                            int alpha = (int) ((1.0F - (float) distance / mConfig.maxDistance) * mConfig.lineAlpha);

                            mLinePaint.setColor(new Color(point.color));
                            mLinePaint.setAlpha(BigDecimal.valueOf(alpha).divide(BigDecimal.valueOf(255), 4, BigDecimal.ROUND_HALF_UP).floatValue());
                            canvas.drawLine(spiderPoint.position[0], spiderPoint.position[1], point.position[0], point.position[1], mLinePaint);
                        }
                    }
                }
                index++;
            }
            canvas.restore();
        }
        getContext().getUITaskDispatcher().asyncDispatch(new Runnable() {
            @Override
            public void run() {
                invalidate();
            }
        });
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        switch (touchEvent.getAction()) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                Log.i("invalidate", "PRIMARY_POINT_DOWN");
                mTouchX = touchEvent.getPointerPosition(touchEvent.getIndex()).getX();
                mTouchY = touchEvent.getPointerPosition(touchEvent.getIndex()).getY();
                break;
            case TouchEvent.POINT_MOVE:
                mTouchX = touchEvent.getPointerPosition(touchEvent.getIndex()).getX();
                mTouchY = touchEvent.getPointerPosition(touchEvent.getIndex()).getY();
                break;
            case TouchEvent.PRIMARY_POINT_UP:
                Log.i("invalidate", "PRIMARY_POINT_UP");
                mTouchX = -1;
                mTouchY = -1;
                break;
        }
        return true;

    }


    /**
     * 两点间距离函数
     *
     * @param pointX1 起点X坐标
     * @param pointY1 起点Y坐标
     * @param pointX2 终点X坐标
     * @param pointY2 终点Y坐标
     * @return int 距离
     */
    public static int disPos2d(float pointX1, float pointY1, float pointX2, float pointY2) {
        return (int) Math.sqrt((pointX1 - pointX2) * (pointX1 - pointX2) + (pointY1 - pointY2) * (pointY1 - pointY2));
    }

    /**
     * 获取范围随机整数：如 rangeInt(1,9)
     *
     * @param start 前数(包括)
     * @param end 后数(包括)
     * @return 范围随机整数
     */
    public static int rangeInt(int start, int end) {
        int max = Math.max(start, end);
        int min = Math.min(start, end) - 1;
        return (int) (min + Math.ceil(mRandom.nextDouble() * (max - min)));
    }

    /**
     * 获取随机颜色
     *
     * @return 获取到随机颜色值
     */
    private int randomRGB() {
        return Color.argb(255, mRandom.nextInt(255), mRandom.nextInt(255), mRandom.nextInt(255));
    }


    // 重置数据
    public void restart() {
        resetTouchPoint();
        clearPointList();
        initPoint();
    }

    /**
     * 重置触摸点
     */
    public void resetTouchPoint() {
        mTouchX = -1;
        mTouchY = -1;
    }

    /**
     * 清空数据源
     */
    private void clearPointList() {
        mSpiderPointList.clear();
    }

    // 设置相关配置参数
    public int getPointRadius() {
        return mConfig.pointRadius;
    }

    public void setPointRadius(int pointRadius) {
        mConfig.pointRadius = pointRadius;
        mPointPaint.setStrokeWidth(mConfig.pointRadius);
    }

    public int getLineWidth() {
        return mConfig.lineWidth;
    }

    public void setLineWidth(int lineWidth) {
        mConfig.lineWidth = lineWidth;
        mLinePaint.setStrokeWidth(mConfig.lineWidth);
    }

    public int getLineAlpha() {
        return mConfig.lineAlpha;
    }

    public void setLineAlpha(int lineAlpha) {
        mConfig.lineAlpha = lineAlpha;
    }

    public int getPointNum() {
        return mConfig.pointNum;
    }

    public void setPointNum(int pointNum) {
        mConfig.pointNum = pointNum;
        restart();
    }

    public int getPointAcceleration() {
        return mConfig.pointAcceleration;
    }

    public void setPointAcceleration(int pointAcceleration) {
        mConfig.pointAcceleration = pointAcceleration;
        restart();
    }

    public int getMaxDistance() {
        return mConfig.maxDistance;
    }

    public void setMaxDistance(int maxDistance) {
        mConfig.maxDistance = maxDistance;
    }

    public int getTouchPointRadius() {
        return mConfig.touchPointRadius;
    }

    public void setTouchPointRadius(int touchPointRadius) {
        mConfig.touchPointRadius = touchPointRadius;
        mTouchPaint.setStrokeWidth(mConfig.touchPointRadius);
    }

    public int getGravitation_strength() {
        return mConfig.gravitation_strength;
    }

    public void setGravitation_strength(int gravitation_strength) {
        mConfig.gravitation_strength = gravitation_strength;
    }


}
